﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using System.Threading;
using System.Collections;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Windows.Forms;
using System.IO.Pipes;

namespace CommLink
{
    /// <summary>
    /// Communication link implementation utilizing Windows named pipes IPC.
    /// </summary>
    public class NamedPipesClientComm : CommLinkAbstract
    {
        /// <summary>
        /// Pipe data transfer length constant.
        /// </summary>
        const int nPipeDataLen = 128;

        /// <summary>
        /// Named pipe for communication with the CCS integrated dll
        /// </summary>
        NamedPipeClientStream nSendPipe;

        /// <summary>
        /// Named pipe for communication with the CCS integrated dll
        /// </summary>
        NamedPipeClientStream nReadPipe;
       
        /// <summary>
        /// Public constructor
        /// </summary>
        public NamedPipesClientComm()
        {
        }

        /// <summary>
        /// Destructor closing the named pipe
        /// </summary>
        ~NamedPipesClientComm()
        {
            closeNamedPipes();
        }
        
        /// <summary>
        /// Closes up anyform of open comm channels
        /// </summary>
        public override void closeConnection()
        {
            closeNamedPipes();
        }

        /// <summary>
        /// Named pipes get shut down.
        /// </summary>
        public void closeNamedPipes()
        {
            terminating = true;

            if (nSendPipe != null && nSendPipe.IsConnected)
                nSendPipe.Close();

            if (nReadPipe != null && nReadPipe.IsConnected)
                nReadPipe.Close();

            if (OnConnectionStatusChange != null)
                OnConnectionStatusChange(ConnectionStates.NotConnected);
        }

        /// <summary>
        /// Sends a data buffer trough the named pipe to the CCS integrated dll
        /// </summary>
        /// <param name="OutData">Byte array to sned</param>
        /// <returns>Error status</returns>
        public override int messageSend(byte[] OutData)
        {
            int status = 0;
            try
            {
                if (OutData.Length > nPipeDataLen)
                {
                    MessageBox.Show("Message exceeds pipe limit of " + nPipeDataLen.ToString());
                    status = -1;
                }
                else
                {
                    byte[] outbandData = new byte[nPipeDataLen];
                    OutData.CopyTo(outbandData, 0);
                    nSendPipe.Write(outbandData, 0, nPipeDataLen);
                }
            }
            catch (Exception ex)
            {
                status = -1;
                MessageBox.Show(ex.Message);
            }

            if (nSendPipe.IsConnected && (status == -1 || terminating))
            {
                closeNamedPipes();
                MessageBox.Show("Server connection lost");
            }

            return status;
        }

        /// <summary>
        /// Pipe reading thread termination flag.
        /// </summary>
        Boolean terminating = false;

        /// <summary>
        /// Pipe reading thread funciton.
        /// </summary>
        void threadReadingFunction()
        {
            while (!terminating && nReadPipe.IsConnected && nReadPipe.CanRead)
            {
                byte[] msgInc = new byte[nPipeDataLen];
                nReadPipe.Read(msgInc, 0, nPipeDataLen);

                if (OnMessageReceived != null)
                    OnMessageReceived(msgInc);
            }
            if (nReadPipe.IsConnected)
                closeNamedPipes();

            MessageBox.Show("Server connection lost");
        }

        /// <summary>
        /// Message received event providing the byte array data received from the pipe.
        /// </summary>
        public override event MessageReceived OnMessageReceived;

        /// <summary>
        /// Connection status change event.
        /// </summary>
        public override event ConnectionStatusChange OnConnectionStatusChange;

        /// <summary>
        /// Opens communication channels
        /// </summary>
        public override void openConnection()
        {
            bool connectFailed = false;

            try
            {
                nSendPipe = new NamedPipeClientStream(Properties.Settings.Default.WinPipes_Server_machine, Properties.Settings.Default.WinPipes_Write_to_named_pipe, PipeDirection.Out);
                nSendPipe.Connect(5000);
            }
            catch (Exception ex)
            {
                MessageBox.Show("Failed to open IPC Named Pipe \"sendHere\"; Reason:\n" + ex.Message);
                connectFailed = true;
            }

            if (!connectFailed)
            {
                try
                {
                    nReadPipe = new NamedPipeClientStream(Properties.Settings.Default.WinPipes_Server_machine, Properties.Settings.Default.WinPipes_Read_from_named_pipe, PipeDirection.In);
                    nReadPipe.Connect(5000);

                    Thread threadReading = new Thread(new ThreadStart(threadReadingFunction));
                    threadReading.IsBackground = true;
                    threadReading.Start();
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Failed to open IPC Named Pipe \"readHere\"; Reason:\n" + ex.Message);
                    connectFailed = true;
                }
            }

            if (OnConnectionStatusChange != null)
            {
                if (connectFailed)
                    OnConnectionStatusChange(ConnectionStates.ServerError);
                else
                    OnConnectionStatusChange(ConnectionStates.Connected);
            }
        }

        /// <summary>
        /// Returns connection type name
        /// </summary>
        /// <returns>Connection type name</returns>
        public override string getName()
        {
            return "windows named pipes to CCS integrated dll";
        }
    }
}
